VT-d: adjust IOMMU interrupt affinities when all CPUs are online
authorJan Beulich <jbeulich@suse.com>
Wed, 28 Nov 2012 09:08:24 +0000 (10:08 +0100)
committerJan Beulich <jbeulich@suse.com>
Wed, 28 Nov 2012 09:08:24 +0000 (10:08 +0100)
Since these interrupts get setup before APs get brought online, their
affinities naturally could only ever point to CPU 0 alone so far.
Adjust this to include potentially multiple CPUs in the target mask
(when running in one of the cluster modes), and take into account NUMA
information (to handle the interrupts on a CPU on the node where the
respective IOMMU is).

Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Keir Fraser <keir@xen.org>
xen/arch/x86/acpi/power.c
xen/drivers/passthrough/vtd/iommu.c
xen/include/xen/iommu.h

index 9e1f98904f40c4023734c7ef7b8d8a1dcdc10779..e60173f9524852cb2e995f8ce96256b1d688dbb2 100644 (file)
@@ -219,6 +219,7 @@ static int enter_state(u32 state)
     mtrr_aps_sync_begin();
     enable_nonboot_cpus();
     mtrr_aps_sync_end();
+    adjust_vtd_irq_affinities();
     acpi_dmar_zap();
     thaw_domains();
     system_state = SYS_STATE_active;
index 8ec9bcddd85d5eefa68eaf6b7fe215f4f9bfc876..5b69c553b147bf24c1717623c9199a08e057a975 100644 (file)
@@ -1971,6 +1971,33 @@ void clear_fault_bits(struct iommu *iommu)
     spin_unlock_irqrestore(&iommu->register_lock, flags);
 }
 
+static void adjust_irq_affinity(struct acpi_drhd_unit *drhd)
+{
+    const struct acpi_rhsa_unit *rhsa = drhd_to_rhsa(drhd);
+    unsigned int node = rhsa ? pxm_to_node(rhsa->proximity_domain)
+                             : NUMA_NO_NODE;
+    const cpumask_t *cpumask = &cpu_online_map;
+
+    if ( node < MAX_NUMNODES && node_online(node) &&
+         cpumask_intersects(&node_to_cpumask(node), cpumask) )
+        cpumask = &node_to_cpumask(node);
+    dma_msi_set_affinity(irq_to_desc(drhd->iommu->msi.irq), cpumask);
+}
+
+int adjust_vtd_irq_affinities(void)
+{
+    struct acpi_drhd_unit *drhd;
+
+    if ( !iommu_enabled )
+        return 0;
+
+    for_each_drhd_unit ( drhd )
+        adjust_irq_affinity(drhd);
+
+    return 0;
+}
+__initcall(adjust_vtd_irq_affinities);
+
 static int init_vtd_hw(void)
 {
     struct acpi_drhd_unit *drhd;
@@ -1984,13 +2011,10 @@ static int init_vtd_hw(void)
      */
     for_each_drhd_unit ( drhd )
     {
-        struct irq_desc *desc;
+        adjust_irq_affinity(drhd);
 
         iommu = drhd->iommu;
 
-        desc = irq_to_desc(iommu->msi.irq);
-        dma_msi_set_affinity(desc, desc->arch.cpu_mask);
-
         clear_fault_bits(iommu);
 
         spin_lock_irqsave(&iommu->register_lock, flags);
index 3026e54606298fdc2c3f094f8f801904a2821498..7626216ae80dd489cf62e58cc47ff631e77fcd31 100644 (file)
@@ -137,6 +137,9 @@ int iommu_do_domctl(struct xen_domctl *, XEN_GUEST_HANDLE_PARAM(xen_domctl_t));
 void iommu_iotlb_flush(struct domain *d, unsigned long gfn, unsigned int page_count);
 void iommu_iotlb_flush_all(struct domain *d);
 
+/* While VT-d specific, this must get declared in a generic header. */
+int adjust_vtd_irq_affinities(void);
+
 /*
  * The purpose of the iommu_dont_flush_iotlb optional cpu flag is to
  * avoid unecessary iotlb_flush in the low level IOMMU code.